﻿using System;

using NUnit.Framework;

using TrueCode.DependencyManager;
using TrueCode.DependencyManager.BuildActions.Injection;
using TrueCode.DependencyManager.BuildActions.Instantiating;
using TrueCode.DependencyManager.Extensions;
using TrueCode.DependencyManager.Extensions.Action.Typed;
using TrueCode.DependencyManager.Interface;
using TrueCode.DependencyManager.Services.InjectionPoints;
using TrueCode.DependencyManager.Services.Instantiating;
using TrueCode.DependencyManager.Services.Parameters;

namespace DependencyManager.Tests
{
  public class AttributedVictim
  {
    [InjectHere]
    public void InjectMethod0()
    {
      InjectMethod0WasCalled = true;
    }

    public bool InjectMethod0WasCalled { get; set; }

    [InjectHere]
    public void InjectMethodIntInt( int p0, int p1 )
    {
      InjectMethodIntIntP0 = p0;
      InjectMethodIntIntP1 = p1;
    }

    public int InjectMethodIntIntP0 { get; set; }
    public int InjectMethodIntIntP1 { get; set; }
  }

  public class AttributedVictimOwner
  {
    public AttributedVictimOwner( AttributedVictim attributeVictim )
    {
      AttributeVictim = attributeVictim;
    }

    public AttributedVictim AttributeVictim { get; set; }
  }

  public class NamedVictim
  {
    public void InjectMethod0()
    {
      InjectMethod0WasCalled = true;
    }

    public void InjectMethod1()
    {
      InjectMethod1WasCalled = true;
    }

    public bool InjectMethod0WasCalled { get; set; }
    public bool InjectMethod1WasCalled { get; set; }
  }

//  public interface IAttributedVictim
//  {
//    [InjectHere]
//    void InjectMethod0();
//  }

  /// <summary>
  /// Summary description for UseCases
  /// </summary>
  [TestFixture]
  public class UseCases
  {
    [Test]
    public void AttributesAsDefaultNamedMethodForConcreteType()
    {
      var container = new Container( typeof( IInstantiate ),
                                     typeof( IInject ) )
        .AddDefaultAction< IInject >( new CallMethodByReflection() )
        .AddDefaultAction< IInstantiate >( new InstantiateByReflection() )
        .AddDefaultService< IConstructorProvider >( new MostParametersCountCtorProvider() )
        .AddDefaultService< IMethodProvider >( new MethodsByAttributeProvider() )
        .AddDefaultService< IParameterProvider >( new AutoParameterProvider() )
        .AddServiceFor< IMethodProvider >( new SessionAim( typeof( NamedVictim ) ),
                                           new MethodByNameProvider( new NamedMethod( "InjectMethod0" ) ) )
        .AddServiceFor< IMethodProvider >( new SessionAim( typeof( NamedVictim ), "1" ),
                                           new MethodByNameProvider( "InjectMethod1", "InjectMethod0" ) );

      var aVictim = container.Resolve< AttributedVictim >();
      var nVictim = container.Resolve< NamedVictim >();
      var n1Victim = container.Resolve< NamedVictim >( "1" );

      Assert.IsTrue( aVictim.InjectMethod0WasCalled );
      Assert.IsTrue( nVictim.InjectMethod0WasCalled );
      Assert.IsTrue( n1Victim.InjectMethod1WasCalled );
      Assert.IsTrue( n1Victim.InjectMethod0WasCalled );
    }


    [Test]
    public void PassParametersIntoDeepBuild()
    {
      var container = new Container( typeof( IInstantiate ),
                                     typeof( IInject ) )
        .AddDefaultAction< IInject >( new CallMethodByReflection() )
        .AddDefaultAction< IInstantiate >( new InstantiateByReflection() )
        .AddDefaultService< IConstructorProvider >( new MostParametersCountCtorProvider() )
        .AddDefaultService< IParameterProvider >( new AutoParameterProvider() )
        .AddDefaultService< IMethodProvider >( new MethodsByAttributeProvider() );

      var services = new ServiceCollection();
      services.Add< AttributedVictim, IParameterProvider >( "InjectMethodIntInt",
                                                            new PositionalParameterProvider(
                                                              new PositionalParameter( 0, 100 ),
                                                              new PositionalParameter( 1, 200 ) ) );
      //TODO: decrement names length

      var owner = container.Resolve< AttributedVictimOwner >( services );

      Assert.AreEqual( 100, owner.AttributeVictim.InjectMethodIntIntP0 );
      Assert.AreEqual( 200, owner.AttributeVictim.InjectMethodIntIntP1 );

      Assert.IsTrue( owner.AttributeVictim.InjectMethod0WasCalled );
    }

//    public void DefaultForSettingBuildActionTest()
//    {
//      var container = new Container( typeof( IInstantiateBuildAction ),
//                                     typeof( IChooseBaseClassMethodBuildAction ),
//                                     typeof( IInjectBuildAction ),
//                                     typeof( IInjectBuildAction ) )
//        .AddDefaultAction< IInstantiateBuildAction >( new InstantiateByReflectionBuildAction() )
//        .AddDefaultAction< IInjectBuildAction >( "baseClass", new CallMethodByReflection() )
//        .AddDefaultAction< IInjectBuildAction >( new CallMethodByReflection() )
//
//        .AddDefaultService< IConstructorProvider >( new MostParametersCountConstructorProvider() )
//        .AddDefaultService< IParameterValueProvider >( new AutoParameterValueProvider() )
//        .AddDefaultService< IMethodsProvider >( new MethodsByAttributeProvider() )
//        
//        .AddActionFor< IChooseBaseClassMethodBuildAction, NamedVictim >( "baseClass", new ChooseBaseClassMethodByName() );
//
//
//      var xz = container.Resolve< NamedVictim >( "baseClass" );
//
//    }

    private class Base
    {
      [InjectHere]
      public virtual void VirtualInject()
      {
      }

      [InjectHere]
      public void Inject()
      {
      }
    }

    private class Inheriter : Base
    {
      public override void VirtualInject()
      {
      }
    }

    [Test]
    public void GetMethodTest()
    {
      var type = typeof( Inheriter );
      var methods = type.GetMethods();

      foreach( var method in methods )
      {
        var attrs = method.GetCustomAttributes( typeof( InjectHereAttribute ), false );
        var inheritedAttrs = method.GetCustomAttributes( typeof( InjectHereAttribute ), true );

        Console.WriteLine( attrs );
        Console.WriteLine( inheritedAttrs );
      }
    }
  }
}